﻿// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using global::PowerToys.GPOWrapper;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
using Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events;
using Microsoft.PowerToys.Settings.UI.Library.ViewModels.Commands;
using Microsoft.PowerToys.Telemetry;

namespace Microsoft.PowerToys.Settings.UI.ViewModels
{
    public class CmdNotFoundViewModel : Observable
    {
        public ButtonClickCommand CheckRequirementsEventHandler => new ButtonClickCommand(CheckCommandNotFoundRequirements);

        public ButtonClickCommand InstallPowerShell7EventHandler => new ButtonClickCommand(InstallPowerShell7);

        public ButtonClickCommand InstallWinGetClientModuleEventHandler => new ButtonClickCommand(InstallWinGetClientModule);

        public ButtonClickCommand InstallModuleEventHandler => new ButtonClickCommand(InstallModule);

        public ButtonClickCommand UninstallModuleEventHandler => new ButtonClickCommand(UninstallModule);

        private GpoRuleConfigured _enabledGpoRuleConfiguration;
        private bool _enabledStateIsGPOConfigured;

        public static string AssemblyDirectory
        {
            get
            {
                string codeBase = Assembly.GetExecutingAssembly().Location;
                UriBuilder uri = new UriBuilder(codeBase);
                string path = Uri.UnescapeDataString(uri.Path);
                return Path.GetDirectoryName(path);
            }
        }

        public CmdNotFoundViewModel()
        {
            InitializeEnabledValue();
        }

        private void InitializeEnabledValue()
        {
            _enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredCmdNotFoundEnabledValue();
            if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled)
            {
                // Get the enabled state from GPO.
                _enabledStateIsGPOConfigured = true;
            }

            CheckCommandNotFoundRequirements();
        }

        private string _commandOutputLog;

        public string CommandOutputLog
        {
            get => _commandOutputLog;
            set
            {
                if (_commandOutputLog != value)
                {
                    _commandOutputLog = value;
                    OnPropertyChanged(nameof(CommandOutputLog));
                }
            }
        }

        private bool _isPowerShell7Detected;

        public bool IsPowerShell7Detected
        {
            get => _isPowerShell7Detected;
            set
            {
                if (_isPowerShell7Detected != value)
                {
                    _isPowerShell7Detected = value;
                    OnPropertyChanged(nameof(IsPowerShell7Detected));
                }
            }
        }

        private bool _isWinGetClientModuleDetected;

        public bool IsWinGetClientModuleDetected
        {
            get => _isWinGetClientModuleDetected;
            set
            {
                if (_isWinGetClientModuleDetected != value)
                {
                    _isWinGetClientModuleDetected = value;
                    OnPropertyChanged(nameof(IsWinGetClientModuleDetected));
                }
            }
        }

        private bool _isCommandNotFoundModuleInstalled;

        public bool IsCommandNotFoundModuleInstalled
        {
            get => _isCommandNotFoundModuleInstalled;
            set
            {
                if (_isCommandNotFoundModuleInstalled != value)
                {
                    _isCommandNotFoundModuleInstalled = value;
                    OnPropertyChanged(nameof(IsCommandNotFoundModuleInstalled));
                }
            }
        }

        public bool IsEnabledGpoConfigured
        {
            get => _enabledStateIsGPOConfigured;
        }

        public bool IsArm64Arch
        {
            get => RuntimeInformation.OSArchitecture == System.Runtime.InteropServices.Architecture.Arm64;
        }

        public string RunPowerShellScript(string powershellExecutable, string powershellArguments, bool hidePowerShellWindow = false)
        {
            string outputLog = string.Empty;
            try
            {
                var startInfo = new ProcessStartInfo()
                {
                    FileName = powershellExecutable,
                    Arguments = powershellArguments,
                    CreateNoWindow = hidePowerShellWindow,
                    UseShellExecute = false,
                    RedirectStandardOutput = true,
                };
                startInfo.EnvironmentVariables["NO_COLOR"] = "1";
                var process = Process.Start(startInfo);
                while (!process.StandardOutput.EndOfStream)
                {
                    outputLog += process.StandardOutput.ReadLine() + "\r\n"; // Weirdly, PowerShell 7 won't give us new lines.
                }
            }
            catch (Exception ex)
            {
                outputLog = ex.ToString();
            }

            CommandOutputLog = outputLog;
            return outputLog;
        }

        public void CheckCommandNotFoundRequirements()
        {
            var ps1File = AssemblyDirectory + "\\Assets\\Settings\\Scripts\\CheckCmdNotFoundRequirements.ps1";
            var arguments = $"-NoProfile -NonInteractive -ExecutionPolicy Unrestricted -File \"{ps1File}\"";
            var result = RunPowerShellScript("pwsh.exe", arguments, true);

            if (result.Contains("PowerShell 7.4 or greater detected."))
            {
                IsPowerShell7Detected = true;
            }
            else if (result.Contains("PowerShell 7.4 or greater not detected."))
            {
                IsPowerShell7Detected = false;
            }
            else if (result.Contains("pwsh.exe"))
            {
                // Likely an error saying there was an error starting pwsh.exe, so we can assume Powershell 7 was not detected.
                CommandOutputLog += "PowerShell 7.4 or greater not detected. Installation instructions can be found on https://learn.microsoft.com/powershell/scripting/install/installing-powershell-on-windows \r\n";
                IsPowerShell7Detected = false;
            }

            if (result.Contains("WinGet Client module detected."))
            {
                IsWinGetClientModuleDetected = true;
            }
            else if (result.Contains("WinGet Client module not detected."))
            {
                IsWinGetClientModuleDetected = false;
            }

            if (result.Contains("Command Not Found module is registered in the profile file."))
            {
                IsCommandNotFoundModuleInstalled = true;
            }
            else if (result.Contains("Command Not Found module is not registered in the profile file."))
            {
                IsCommandNotFoundModuleInstalled = false;
            }

            Logger.LogInfo(result);
        }

        public void InstallPowerShell7()
        {
            var ps1File = AssemblyDirectory + "\\Assets\\Settings\\Scripts\\InstallPowerShell7.ps1";
            var arguments = $"-NoProfile -ExecutionPolicy Unrestricted -File \"{ps1File}\"";
            var result = RunPowerShellScript("powershell.exe", arguments);
            if (result.Contains("Powershell 7 successfully installed."))
            {
                IsPowerShell7Detected = true;
            }

            Logger.LogInfo(result);

            // Update PATH environment variable to get pwsh.exe on further calls.
            Environment.SetEnvironmentVariable("PATH", (Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Machine) ?? string.Empty) + ";" + (Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.User) ?? string.Empty), EnvironmentVariableTarget.Process);
        }

        public void InstallWinGetClientModule()
        {
            var ps1File = AssemblyDirectory + "\\Assets\\Settings\\Scripts\\InstallWinGetClientModule.ps1";
            var arguments = $"-NoProfile -ExecutionPolicy Unrestricted -File \"{ps1File}\"";
            var result = RunPowerShellScript("pwsh.exe", arguments);
            if (result.Contains("WinGet Client module detected."))
            {
                IsWinGetClientModuleDetected = true;
            }
            else if (result.Contains("WinGet Client module not detected."))
            {
                IsWinGetClientModuleDetected = false;
            }

            Logger.LogInfo(result);
        }

        public void InstallModule()
        {
            var ps1File = AssemblyDirectory + "\\Assets\\Settings\\Scripts\\EnableModule.ps1";
            var arguments = $"-NoProfile -ExecutionPolicy Unrestricted -File \"{ps1File}\" -scriptPath \"{AssemblyDirectory}\\..\"";
            var result = RunPowerShellScript("pwsh.exe", arguments);

            if (result.Contains("Module is already registered in the profile file.") || result.Contains("Module was successfully registered in the profile file."))
            {
                IsCommandNotFoundModuleInstalled = true;
                PowerToysTelemetry.Log.WriteEvent(new CmdNotFoundInstallEvent());
            }

            Logger.LogInfo(result);
        }

        public void UninstallModule()
        {
            var ps1File = AssemblyDirectory + "\\Assets\\Settings\\Scripts\\DisableModule.ps1";
            var arguments = $"-NoProfile -ExecutionPolicy Unrestricted -File \"{ps1File}\"";
            var result = RunPowerShellScript("pwsh.exe", arguments);

            if (result.Contains("Removed the Command Not Found reference from the profile file.") || result.Contains("No instance of Command Not Found was found in the profile file."))
            {
                IsCommandNotFoundModuleInstalled = false;
                PowerToysTelemetry.Log.WriteEvent(new CmdNotFoundUninstallEvent());
            }

            Logger.LogInfo(result);
        }
    }
}
